#include <fstream>
#include <iostream>
#include <string>

#define VSCM_VER "v1.1.3"

#if defined(__clang__) && defined(__APPLE__)
#include <mach-o/dyld.h>
#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
#include <windows.h>
#else
#include <unistd.h>
#endif

#if defined(_MSC_VER) && !defined(__MINGW__)
#define CPP_STANDARD _MSVC_LANG
#else
#define CPP_STANDARD __cplusplus
#endif

#if CPP_STANDARD >= 201703L // 检查C++17或更高版本支持
#include <filesystem>
namespace fs = std::filesystem;
#elif CPP_STANDARD >= 201402L // 检查C++14或更高版本支持
#include <filesystem>
namespace fs = std::experimental::filesystem;
#else
#error "不支持的C++标准版本"
#endif

using ToolPath = fs::path;
bool writeTxt(const ToolPath &path, const std::string &content) {

  std::ofstream file(path.string());
  if (!file) {
    std::cout << "打开文件失败: " << path.string() << std::endl;
    return false;
  }
  file << content;
  file.close();
  return true;
}

bool readOneLineTxt(const ToolPath &path, std::string &content) {

  content.clear();
  std::ifstream file(path.string());

  if (!file) {
    std::cout << "打开文件失败: " << path.string() << std::endl;
    return false;
  }
  file >> content;
  file.close();
  return true;
}

std::string getCurrentPath();
std::string getSDKVersion() {

  std::string reVersion;
  // 从 ver.txt 中获取版本号
  ToolPath verPath = ToolPath(getCurrentPath()).append(std::string("ver.txt")).string();
  readOneLineTxt(verPath, reVersion);
  // HKEY hKey;
  // DWORD dataSize = 0;
  // DWORD valueType;
  // TCHAR version[MAX_PATH];
  // // 打开 Windows SDK 注册表项
  // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Microsoft
  // SDKs\\Windows"), 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
  //     // 获取当前版本的值数据大小
  //     if (RegQueryValueEx(hKey, TEXT("CurrentVersion"), NULL, &valueType,
  //     NULL, &dataSize) == ERROR_SUCCESS) {
  //         // 读取当前版本号
  //         if (RegQueryValueEx(hKey, TEXT("CurrentVersion"), NULL, &valueType,
  //         reinterpret_cast<LPBYTE>(version), &dataSize) == ERROR_SUCCESS) {
  //             reVersion = std::string(version);
  //         }
  //     }
  //     // 关闭注册表项
  //     RegCloseKey(hKey);
  // }
  return reVersion;
}

void writeCppProperties(const ToolPath &path) {

  std::string platName;
#if _WIN32
  platName = "Win32";
#else
  platName = "UnixPlat";
#endif
  std::string content;
  content.append("{\n"
                 "   \"configurations\": [\n"
                 "       {\n"
                 "           \"name\": \"");
  content.append(platName);
  content.append("\",\n"
                 "           \"includePath\": [\n"
                 "               \"${workspaceFolder}/**\"\n"
                 "           ],\n"
                 "           \"defines\": [\n"
                 "               \"_DEBUG\",\n"
                 "               \"UNICODE\",\n"
                 "               \"_UNICODE\"\n"
                 "           ],\n"
                 "           \"windowsSdkVersion\": \"");
  std::string ver = getSDKVersion();
  if (ver.empty()) {
    ver.append("10.0.17134.0");
  }
  content.append(getSDKVersion());
  content.append(
      "\",\n"
      "           \"compilerPath\": \"cl.exe\",\n"
      "           \"cStandard\": \"c11\",\n"
      "           \"cppStandard\": \"c++17\",\n"
      "           \"intelliSenseMode\": \"windows-msvc-x64\",\n"
      "           \"compileCommands\": "
      "\"${workspaceFolder}/build/compile_commands.json\",\n"
      "           \"configurationProvider\": \"ms-vscode.cmake-tools\"\n"
      "       }\n"
      "   ],\n"
      "   \"version\": 4\n"
      "}");
  std::string cpp_properties =
      ToolPath(path).append(std::string("c_cpp_properties.json")).string();
  if (writeTxt(cpp_properties, content)) {
    std::cout << "写入 c_cpp_properties.json 成功.." << std::endl;
  }
}

std::string getCurrentPath() {

#ifdef _WIN32
  char exePath[1024] = "";
  GetModuleFileName(NULL, exePath, sizeof(exePath));
  return ToolPath(exePath).parent_path().string();
#elif defined(__clang__) && defined(__APPLE__)
  char executablePath[PATH_MAX];
  uint32_t size = sizeof(executablePath);
  _NSGetExecutablePath(executablePath, &size);
  return std::string(executablePath);
#else
  char resolvedPath[512];
  ssize_t length =
      readlink("/proc/self/exe", resolvedPath, sizeof(resolvedPath) - 1);
  return std::string(resolvedPath);
#endif
}

// vm D:\ cpptest w 后面的参数各是 是否clangd，是否ninja
int main(int argc, char **argv) {
  if (argc < 6) {
    std::cout << "没有给定足够的参数... " << VSCM_VER << std::endl;
    std::cout << "vscm D:/code cppdemo w (w:MSVC m:MinGW64 u:Linux)" << std::endl;
    std::cout
        << "若是 GNU 或 Clang 系列请在 vm 所在目录下的 ini.txt 配置编译器路径。"
        << std::endl;
    std::cout << "若是 WIN 则同位置在 ver.txt 中填写 SDK "
                 "版本号。\n后面的参数各是:是否clangd, 是否ninja。"
              << std::endl;
    return 0;
  }
  bool isClangd = false;
  bool isNinja = false;

  if (std::string(argv[4]) == "y" || std::string(argv[4]) == "Y") {
    isClangd = true;
  }
  if (std::string(argv[5]) == "y" || std::string(argv[5]) == "Y") {
    isNinja = true;
  }

  const char *desParentDir = argv[1];

  std::cout << "选择的平台是：" << argv[3] << std::endl;

  ToolPath path(desParentDir);
  if (!fs::exists(path)) {
    std::cout << "给定的路径不存在: " << desParentDir << std::endl;
    return 0;
  }

  ToolPath desPath = path.append(argv[2]);
  if (fs::exists(desPath)) {
    std::cout << "指定的文件夹已存在: " << desPath << std::endl;
    return 0;
  }

  if (fs::create_directory(desPath)) {
    std::cout << "创建文件夹成功: " << desPath << std::endl;
  } else {
    std::cout << "创建文件夹失败: " << desPath << std::endl;
    return 0;
  }
  std::string mainContent;
  mainContent.append("#include <iostream>\n\n"
                     "int main(int argc, char** argv) {\n\n"
                     "    std::cout << \"Hello Cpp!\" << std::endl;\n"
                     "    return 0;\n"
                     "}");

  ToolPath mainFile = ToolPath(desPath).append(std::string("main.cpp"));
  if (!writeTxt(mainFile, mainContent)) {
    return 0;
  }
  std::cout << "写入 main.cpp 成功。" << std::endl;

  std::string cmakeLists;
  cmakeLists.append("cmake_minimum_required (VERSION 3.8)\n\n"
                    "project (");
  cmakeLists.append(argv[2]);
  cmakeLists.append(
      ")\n"
      "set(CMAKE_CXX_STANDARD 11)\n\n"
      "if (MSVC)\n"
      "    string(APPEND CMAKE_CXX_FLAGS  \" /source-charset:utf-8 /EHsc\")\n"
      "endif()\n\n"
      "add_executable(");
  cmakeLists.append(argv[2]);
  cmakeLists.append(" main.cpp)");

  ToolPath cmakeListFile = ToolPath(desPath).append(std::string("CMakeLists.txt"));
  if (!writeTxt(cmakeListFile, cmakeLists)) {
    return 0;
  }
  std::cout << "写入 CMakeLists.txt 成功。" << std::endl;

  // 创建 .vscode 文件夹
  ToolPath vscodeDir = ToolPath(desPath).append(std::string(".vscode"));
  if (fs::create_directory(vscodeDir)) {
    std::cout << "创建文件夹成功: " << vscodeDir << std::endl;
  } else {
    std::cout << "创建文件夹失败: " << vscodeDir << std::endl;
    return 0;
  }

  // 读取 g++ 路径
  ToolPath currentPath(argv[0]);
  std::cout << "vscm 所在目录：" << getCurrentPath() << std::endl;
  
  std::string szGccPath;
  if (std::string(argv[3]) != "w") {
      ToolPath iniFile = ToolPath(getCurrentPath()).append(std::string("ini.txt"));
      readOneLineTxt(iniFile, szGccPath);
      if (szGccPath.empty()) {
        szGccPath.append("/usr/bin/g++");
      }
  }

  ToolPath settingFile = ToolPath(vscodeDir).append(std::string("settings.json"));
  std::string settingContent;
  settingContent.append("{\n"
                        "    \"files.autoSave\": \"onFocusChange\",\n"
                        "    \"editor.fontSize\": 16,\n"
                        "    \"editor.fontFamily\": \"'Fira Code', 'Fira Code', 'Fira Code'\",\n"
                        "    \"cmake.configureOnOpen\": true,\n");
  if (std::string(argv[3]) == "m") {
    settingContent.append(
        "    \"cmake.debugConfig\": {\n"
        "        //\"console\": \"externalTerminal\",\n"
        "        \"setupCommands\": [\n"
        "            {\n"
        "                \"description\": \"-gdb-set charset utf-8\",\n"
        "                \"text\": \"-gdb-set charset UTF-8\"\n"
        "            },\n"
        "            {\n"
        "                \"description\": \"Enable gdb pretty-printing\",\n"
        "                \"text\": \"-enable-pretty-printing\",\n"
        "                \"ignoreFailures\": true\n"
        "           }\n"
        "        ]\n"
        "    },\n");
  } else if (std::string(argv[3]) == "w") {
    settingContent.append("    \"cmake.debugConfig\": {\n"
                          "        \"console\": \"integratedTerminal\"\n"
                          "    },\n");
    if (isNinja) {
      settingContent.append("    \"cmake.generator\": \"Ninja\",\n");
    }
  }
  if (isClangd) {
    settingContent.append("    \"editor.inlayHints.enabled\": \"off\",\n"
                          "    \"C_Cpp.intelliSenseEngine\": \"disabled\",\n"
                          "    \"clangd.arguments\": [\n"
                          "         \"--header-insertion=never\",\n"
                          "         \"--all-scopes-completion\",\n"
                          "         \"--completion-style=detailed\",\n"
                          "         \"--clang-tidy\",\n"
                          "         \"-j=4\",\n"
                          "         \"--pch-storage=memory\",\n"
                          "         \"--compile-commands-dir=build\",\n"
                          "         \"--background-index\",\n"
                          "         \"--ranking-model=heuristics\",\n");
    if (std::string(argv[3]) != "w") {
      settingContent.append("        \"--query-driver=");
      settingContent.append(szGccPath);
      settingContent.append("\"\n");
    }
  }
  if (isClangd) {
    settingContent.append("    ]\n");
  }
  settingContent.append("}");
  if (!writeTxt(settingFile, settingContent)) {
    return 0;
  }
  std::cout << "写入 settings.json 成功。" << std::endl;

  if (std::string(argv[3]) == "w" && !isClangd) {
    //writeCppProperties(vscodeDir);
  }
  return 0;
}
